home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / 07.04 Apr 91 / Assoc. Arrays / GameMonitor.st next >
Encoding:
Text File  |  1990-04-22  |  13.6 KB  |  495 lines  |  [TEXT/MACV]

  1. "*************************************************"
  2. "* special classes to override built-in behavior *"
  3. "*                                               *"
  4. "* MyButtonPane bypasses the checks for 'text    *"
  5. "* modified' when a button is pressed, and       *"
  6. "* MyGraphPane eliminates scroll bars on the     *"
  7. "* pane.                                         *"
  8. "*************************************************"
  9.  
  10. ButtonPane subclass: #MyButtonPane
  11.   instanceVariableNames: ''
  12.   classVariableNames: ''
  13.   poolDictionaries: '' !
  14.  
  15. !MyButtonPane class methods ! !
  16.  
  17.  
  18. !MyButtonPane methods !
  19.  
  20. selectAtCursor
  21.     "Press the button at the current cursor position."
  22.     | |
  23.     1 to: boxes size do: [ :i |
  24.         ((boxes at: i) containsPoint: Cursor offset)
  25.             ifTrue: [ ^ self buttonPressed: i ]
  26.     ].! !
  27.  
  28.  
  29. GraphPane subclass: #MyGraphPane
  30.   instanceVariableNames: ''
  31.   classVariableNames: ''
  32.   poolDictionaries: '' !
  33.  
  34. !MyGraphPane class methods ! !
  35.  
  36.  
  37. !MyGraphPane methods !
  38.  
  39. addMenus: menuBar
  40.     "dummy for addSubPane"
  41.     "needed to eliminate scroll bars on GraphPane"
  42.     | |! !
  43.  
  44. "********************************"
  45. "* begin Game Monitor           *"
  46. "********************************"
  47.  
  48. Object subclass: #GameMonitor
  49.   instanceVariableNames: ''
  50.   classVariableNames:
  51.     'CatWins ActivePlayers PromptPane MoveRequestor 
  52.      AllPlayers LogPane GetMovePane TheBoard WhoseMove 
  53.      GameOver '
  54.   poolDictionaries: '' !
  55.  
  56. !GameMonitor class methods !
  57.  
  58. initialize: aBoard
  59.         "Create the monitor panes with aBoard as model,
  60.          also initialize any variables whose value 
  61.          persists across games."
  62.     | topPane |
  63.     (topPane := TopPane new) label: 'Monitor'.
  64.     topPane addSubpane:
  65.         (PromptPane := MyGraphPane new model: aBoard;
  66.             name: #dummyUpdate1:;
  67.             framingRatio: (0@0 extent: 2/3 @ (1/6))).
  68.     topPane addSubpane:
  69.         (GetMovePane := TextPane new model: aBoard;
  70.             name: #dummyUpdate;
  71.             framingRatio: 
  72.                 (0@(1/6) extent: 2/3 @ (1/6))).
  73.     topPane addSubpane:
  74.         (LogPane := TextPane new model: aBoard;
  75.             name: #dummyUpdate;
  76.             framingRatio: (0@(1/3) extent: 1@(2/3))).
  77.     topPane addSubpane:
  78.         (MyButtonPane new model: aBoard;
  79.             buttons: #(Move);
  80.             change: #readMove:;
  81.             pulse: true;
  82.             framingRatio: 
  83.                 (2/3 @ 0 extent: 1/3 @ (1/3))).
  84.  
  85.     "initialize persistent values"
  86.     CatWins := 0.
  87.     TheBoard := aBoard.! !
  88.  
  89.  
  90. !GameMonitor methods !
  91.  
  92. dummyUpdate
  93.         "private - do nothing to update TextPane"
  94.     | |
  95.     ^'' "have to send back something, or it won't work"!
  96.  
  97. dummyUpdate1: aRect
  98.         "private - initialize form for GraphPane"
  99.     | aForm |
  100.     aForm := Form
  101.         width: aRect width
  102.         height: aRect height.
  103.         aForm white; offset: aRect origin.
  104.     ^aForm.!
  105.  
  106. gameOver
  107.         "private - called from moveOver if 
  108.          the game is now over"
  109.     | playAgain |
  110.     self loggit: '---game over'.
  111.     "ask for another game"
  112.     self loggit:
  113.         'Scores: (Cat got ',
  114.             (CatWins printPaddedTo: 4) , ')'.
  115.     AllPlayers do: [:aPlayer | aPlayer printScore].
  116.  
  117.     "To have the computer play itself continuously, the
  118.      following statement should be replaced with
  119.         playAgain := 'Yes'."
  120.     playAgain :=Prompter prompt: 'Play again?'
  121.                     default: 'Yes'.
  122.     (playAgain = 'Yes')
  123.         ifTrue: [ TheBoard reset. self restartPlayers ]
  124.         ifFalse: [self loggit: '***play is over'.
  125.                   "this releases the players and board"
  126.                   AllPlayers := nil.
  127.                   TheBoard := nil.].!
  128.  
  129. loggit: aString
  130.         "write aString to the LogPane, supplying the Cr"
  131.     | |
  132.     LogPane appendString: aString;
  133.             appendChar: (CharacterConstants at: 'Cr');
  134.             displayChanges.!
  135.  
  136. moveOver
  137.         "This is the main loop of the monitor.  If the 
  138.          game is not over yet, it determines the next 
  139.          active player and tells him to make a move.  
  140.          If the game is over, it so states, prints 
  141.          statistics, and asks if you want to play 
  142.          again."
  143.  
  144.         "A game is over either when one player declares
  145.          himself the winner, or if all players have 
  146.          resigned."
  147.     | |
  148.     TheBoard showBoard.
  149.     GameOver
  150.         ifFalse: [ "move to next player"
  151.                     WhoseMove := WhoseMove \\ 
  152.                                  (AllPlayers size) + 1.
  153.                     [ActivePlayers at: WhoseMove] 
  154.                         whileFalse:
  155.                         [WhoseMove := WhoseMove \\ 
  156.                                  (AllPlayers size) + 1].
  157.                     (AllPlayers at: WhoseMove) yourMove.
  158.                 ]
  159.         ifTrue: [ self gameOver ].!
  160.  
  161. readMove: whichButton
  162.         "private - Send the move read (the entire text)
  163.          to the requestor.  Argument whichButton is not
  164.          used, since there's only one button"
  165.     | holdRequestor theMove |
  166.     holdRequestor := MoveRequestor.
  167.     theMove := GetMovePane contents.
  168.     "kludge to eliminate trailing Cr"
  169.     ((theMove at: (theMove size)) = 
  170.     (CharacterConstants at: 'Cr'))
  171.         ifTrue: [theMove := 
  172.              theMove copyFrom:1 to: (theMove size - 1)].
  173.     "now clear the panes, and the requestor"
  174.     PromptPane form white. 
  175.     PromptPane update; showWindow.
  176.     GetMovePane selectAll; replaceWithText: ''; update.
  177.     MoveRequestor := nil.
  178.     holdRequestor haveProposedMove: theMove.!
  179.  
  180. requestMove: aPrompt
  181.         "request the human player to make a move 
  182.          by saying aPrompt"
  183.     | aPen |
  184.     MoveRequestor := self.
  185.     (Pen new: (PromptPane form))
  186.         defaultNib: 1;
  187.         place: ((PromptPane form extent) // 2);
  188.         centerText: aPrompt 
  189.             font: (Font applicationFont).
  190.     PromptPane showWindow.
  191.     "the move wil be returned in a haveProposedMove 
  192.      message"!
  193.  
  194. resign
  195.         "A player resigns from the game, or admits 
  196.          defeat.  If all players resign, the Cat wins"
  197.     | |
  198.     self loggit: (self name) , ' says he resigns ' .
  199.     ActivePlayers at: WhoseMove put: false.
  200.     "game is over if there are no move players"
  201.     (ActivePlayers includes: true)
  202.         ifFalse: [GameOver := true.
  203.                     CatWins := CatWins + 1.].
  204.     self moveOver.!
  205.  
  206. restartPlayers
  207.         "private - start players at beginning of game"
  208.     | |
  209.     GameOver := false.
  210.     1 to: (AllPlayers size) do: [:i |
  211.             ActivePlayers at: i put: true].
  212.     AllPlayers do: [:aPlayer |
  213.                         aPlayer newGame].
  214.     WhoseMove := 1.
  215.     (AllPlayers at: WhoseMove) yourMove.!
  216.  
  217. startPlay: allPlayers
  218.         "record the Array of all Players"
  219.         "call the first player"
  220.     | topPane |
  221.     topPane := LogPane topPane.
  222.     topPane dispatcher open.
  223.     AllPlayers := allPlayers.
  224.     ActivePlayers := Array new: (allPlayers size).
  225.     self restartPlayers.
  226.     topPane dispatcher scheduleWindow.!
  227.  
  228. win
  229.         "declare oneself the winner"
  230.     | |
  231.     self loggit: (self name) , ' says he wins'.
  232.     GameOver := true.
  233.     "notify all players of status"
  234.     AllPlayers do: [:aPlayer |
  235.         (aPlayer = self)
  236.             ifTrue: [aPlayer youWin]
  237.             ifFalse: [aPlayer youLose]].
  238.     self moveOver.! !
  239.  
  240. "******************************"
  241. "* GameBoard class definition *"
  242. "******************************"
  243.  
  244. GameMonitor subclass: #GameBoard
  245.   instanceVariableNames:
  246.     'width height positions '
  247.   classVariableNames: ''
  248.   poolDictionaries: '' !
  249.  
  250. !GameBoard class methods ! !
  251.  
  252.  
  253. !GameBoard methods !
  254.  
  255. allLegalMoves
  256.         "answer an OrderedCollection of
  257.          all valid moves from this position"
  258.     | |
  259.     self implementedBySubclass.!
  260.  
  261. getPositions
  262.         "answer a copy of the array of the 
  263.          board position"
  264.     | |
  265.     ^ positions deepCopy.!
  266.  
  267. move: m
  268.         "Record a move by player WhoseMove"
  269.         "Answer:
  270.                 #Win,   if the player wins on this move
  271.                 #Ok,    if this is a legal move
  272.                 #Error, if this is an illegal move 
  273.                         (and do not record the move)"
  274.     | |
  275.     self implementedBySubclass.!
  276.  
  277. reset
  278.         "reset the board back to the start"
  279.     | |
  280.     self implementedBySubclass.!
  281.  
  282. setWidth: w height: h
  283.         "private - initialize board dimensions"
  284.     | |
  285.     width := w.
  286.     height := h.!
  287.  
  288. showBoard
  289.         "display the current board position"
  290.         "subclasses may override this
  291.          to get a different display"
  292.     | oneLine aPlayer |
  293.     1 to: height do:
  294.         [:row | oneLine := ''.
  295.                 1 to: width do:
  296.                     [:col |
  297.                         aPlayer := positions at: 
  298.                                 width*(row - 1) + col.
  299.                         aPlayer isNil
  300.                             ifTrue:
  301.                                 [aPlayer := '.']
  302.                             ifFalse:
  303.                                 [aPlayer := 
  304.                      (AllPlayers at: aPlayer) marker].
  305.                         oneLine := oneLine , aPlayer.
  306.                     ].
  307.                     self loggit: oneLine.
  308.         ]! !
  309.  
  310. "******************************"
  311. "* Player class definition    *"
  312. "******************************"
  313.  
  314. GameMonitor subclass: #Player
  315.   instanceVariableNames:
  316.     'gamesWon whoAmI marker '
  317.   classVariableNames: ''
  318.   poolDictionaries: '' !
  319.  
  320. !Player class methods !
  321.  
  322. new: aName marker: aMarker
  323.         "create a new instance for player aName;
  324.          aMarker will mark his pieces on the board"
  325.     | aPlayer |
  326.     aPlayer := super new.
  327.     aPlayer name: aName marker: aMarker.
  328.     aPlayer clear.
  329.     ^ aPlayer! !
  330.  
  331.  
  332. !Player methods !
  333.  
  334. clear
  335.         "private - clear any needed variables"
  336.     | |
  337.     gamesWon := 0.!
  338.  
  339. haveProposedMove: aMove
  340.         "send the proposed move, yielded by
  341.          requestMove:, to the original requestor"
  342.     | |
  343.     self implementedBySubclass!
  344.  
  345. marker
  346.         "answer the marker of this player"
  347.     | |
  348.     ^ marker.!
  349.  
  350. name
  351.         "answer the player's name"
  352.     | |
  353.     ^ whoAmI!
  354.  
  355. name: aName marker: aMarker
  356.         "private - record name and marker of new player"
  357.     | |
  358.     whoAmI := aName.
  359.     marker := aMarker.!
  360.  
  361. newGame
  362.         "reinitialize for new game - 
  363.          subclasses may supplement this"
  364.     | |!
  365.  
  366. printScore
  367.         "private - print the number of games won 
  368.          on the LogPane"
  369.     | |
  370.     self loggit: whoAmI , (gamesWon printPaddedTo: 4).!
  371.  
  372. youLose
  373.         "Sent to player at end of game, if he lost."
  374.         "May be supplemented in subclass."
  375.     | |!
  376.  
  377. yourMove
  378.         "tells a Player it is his move"
  379.     | |
  380.     self implementedBySubclass!
  381.  
  382. youWin
  383.         "Sent to player at end of game, if he won."
  384.         "May be supplemented in subclass."
  385.     | |
  386.     gamesWon := gamesWon + 1.! !
  387.  
  388.  
  389. Player subclass: #ComputerPlayer
  390.   instanceVariableNames:
  391.     'matchboxes lastMove lastBoardPosition '
  392.   classVariableNames: ''
  393.   poolDictionaries: '' !
  394.  
  395. !ComputerPlayer class methods !
  396.  
  397. new: aName marker: aMarker
  398.         "create a new ComputerPlayer"
  399.     | aPlayer |
  400.     aPlayer := super new: aName marker: aMarker.
  401.     aPlayer createMatchboxes.
  402.     ^aPlayer.! !
  403.  
  404.  
  405. !ComputerPlayer methods !
  406.  
  407. createMatchboxes
  408.         "private - create the Dictionary 
  409.          of matchboxes upon new:"
  410.     |  |
  411.     matchboxes := Dictionary new.!
  412.  
  413. newGame
  414.         "clear detritus from previous game"
  415.     | |
  416.     lastMove := nil.
  417.     lastBoardPosition := nil.!
  418.  
  419. "**********************************************"
  420. "* The matchboxes are implemented in youLose  *"
  421. "* and yourMove.                              *"
  422. "**********************************************"
  423. youLose
  424.         "delete the losing move from the matchboxes"
  425.     | tempMoves |
  426.     lastBoardPosition isNil
  427.         ifTrue: 
  428.             [self error: 'ComputerPlayer can''t move']
  429.         ifFalse: 
  430.             [tempMoves := 
  431.                     (matchboxes at: lastBoardPosition)
  432.                                  deepCopy.
  433.              tempMoves remove: lastMove.
  434.                 matchboxes at: lastBoardPosition
  435.                            put: tempMoves.
  436.             ]. !
  437.  
  438. yourMove
  439.         "generate the next move for this player"
  440.     | theMoves copyBoardPosition moveResult |
  441.     copyBoardPosition := TheBoard getPositions.
  442.     (matchboxes includesKey: copyBoardPosition)
  443.         ifFalse: [ "new position - add all 
  444.                     possible moves"
  445.             matchboxes at: copyBoardPosition
  446.                        put: (TheBoard allLegalMoves)
  447.             ].
  448.     theMoves := matchboxes at: copyBoardPosition.
  449.     ((theMoves size)=0)
  450.         ifTrue: [ "we are blocked - resign"
  451.             self resign. ^nil]
  452.         ifFalse: [
  453.             "pick a move at random, and remember the 
  454.              move in case it is a loser"
  455.             lastMove := theMoves at:
  456.                 (1 + (SmallInteger random: 
  457.                         (theMoves size))).
  458.             lastBoardPosition := copyBoardPosition.
  459.             moveResult := (TheBoard move: lastMove).
  460.             (moveResult = #Win)
  461.                 ifTrue: [self win]
  462.                 ifFalse:[ (moveResult = #Ok)
  463.                             ifTrue: [ self moveOver ]
  464.                            ifFalse:
  465.                                 ["no good - 
  466.                                     internal error"
  467.                                  self error: 
  468.                                      'ComputerPlayer ' ,
  469.                                      'attempted ',
  470.                                      'illegal move' ].
  471.                         ]
  472.                     ]! !
  473.  
  474.  
  475. Player subclass: #HumanPlayer
  476.   instanceVariableNames: ''
  477.   classVariableNames: ''
  478.   poolDictionaries: '' !
  479.  
  480. !HumanPlayer class methods ! !
  481.  
  482.  
  483. !HumanPlayer methods !
  484.  
  485. retryMove
  486.         "ask human to try again - his move was no good"
  487.     | |
  488.     self loggit: 'Try again!!'; yourMove.!
  489.  
  490. yourMove
  491.         "ask the human for his move;
  492.          it will be returned in a 
  493.          haveProposedMove message"
  494.     | |
  495.     self requestMove: whoAmI , '''s move?'! !